/* const count: number = 100
console.log(count) */
//ts类型注解
/* let msg: string
msg = 'this is messagge'
msg = 100 */
//ts简单类型注解
/* let count: number
count = 100
let isLading: boolean
isLading = false */
//ts数组类型注解
//语法1：类型[]
/* let arr1: number[]  //表示变量里只能存放数组，并且数组里每项都是number类型
arr1 = [122, 200] */
//语法2；Array<number>
/* let arr2: Array<number>
arr2 = [222, 300] */
//问题：有一个变量arr，要求用两种方式添加类型注解，使其只能赋值一个成员都是字符串的数组
/* let arr:string[]
arr = ['222', '333']
let arr2:Array<string>
arr2 = ['222', 'false'] */
//联合类型
/* let arr1: (string | number)[] = ['jack', 100] //必须是数组类型，数组里可以为string和number
let arr2:string | number[] //可以是string和数组类型，数组里必须为number类型 */
//类型别名
/* type ItemType = (string | number)[]
let arr: ItemType
let arr2: ItemType
arr = ['jack', 100, 'true']
arr2 = ['jack', 100] */
//问题：有一个变量foo，要求添加类型注解，使其既可以赋值为number类型，也可以赋值为成员都是字符串的数组
/* let foo: string[] | number
foo = ['fff']
foo = 200 */

//函数类型注解
//函数声明
/* function add(a, b) {
    return a + b
} */
//改写为ts
function add(a: number, b: number): number {
    return a + b
}
console.log(add(1, 2))
//修改为箭头函数const add = (a, b) => a + b
//函数表达式
//分开注解
const add2 = (a: number, b: number): number => {
    return a + b
}
console.log(add2(2, 3))
//整体注解（库文件使用）
type AddFn = (a: number, b: number) => number
const add3: AddFn = (a, b) => {
    return a + b
}
console.log(add3(5, 6))
//可选参数(必须在所有参数的末尾)
function buildName(firstName: string, lastName?: string): string {
    if (lastName) {
        return `${firstName}${lastName}`
    } else {
        return firstName
    }
}
console.log(buildName('a'))
console.log(buildName('a', 'b'))
//无返回值注解
function eachArr(arr: number[]): void {
    arr.forEach((item) => console.log(item))
}
const result1 = eachArr([1, 2, 3])
console.log(result1)
//问题：编写一个arr2Str函数，作用为把数组转换为字符串，其中数组中既可以包含字符串和数字，分隔符也可以进行自定义，
//类型为字符串类型，使用样例： 1.arr2Str([1, 2, 3], '-') ---> '1-2-3'    2.arr2Str(['4', '5'], '&') ---> '4&5'
function arr2Str(arr: (string | number)[], test: string): string {
    return arr.join(test)
}
console.log(arr2Str([1,2,3], '-'))
console.log(arr2Str(['1','2',3], '&'))

//interface接口类型
//基础语法
interface Person {
    name: string,
    age: number
}
let p: Person = {
    name: 'jack',
    age: 18
}
//表单数据收集场景说明
interface LoginForm {
    username: string,
    password: string
}
let form:LoginForm = {
    username: 'jack',
    password: '123456'
}
//渲染后端列表场景说明
interface Goods {
    id: string,
    price: number
}
let list:Goods[] = [
    {
        id: '1001',
        price: 200
    }
]
list.forEach((item) => console.log(item.id))
//可选设置
interface Props {
    type: string,
    size?: string
}
let props: Props = {
    type: 'success'
}
props = {
    type: 'success',
    size: 'large'
}
//接口继承
//父接口
interface GoodsType {
    id: string,
    price: number
}
//子接口
interface DisGoodsType extends GoodsType {
    disPrice: number
}
let goods: DisGoodsType = {
    id: '1001',
    price: 200,
    disPrice: 180
}
//问题：通常我们的后端接口返回的数据格式具有一定的规范，比如经常见到的response对象，如下：尝试使用interface接口定义其类型
// {
//     code: 200,
//     msg: 'success',
//     data: {
//         title: '文章标题',
//         content: '文章内容'
//     }
// }
//但是常写方式是先写内层再写外层
interface Data {
    title: string,
    content: string
}
interface DisData {
    code: number,
    msg: string,
    data: Data
}
let res: DisData = {
    code: 200,
    msg: '消息',
    data: {
        title: '文章标题',
        content: '内容'
    }
}
console.log(res.data.title)

//type类型注解
//type实现对象类型注解
type Person2 = {
    name: string
    age?: number
}
let p2: Person2 = {
    name: 'jack',
    age: 18
}
p2 = {
    name: 'jack'
}
//type模拟继承（交叉类型&）
//父接口
type GoodsType2 = {
    id: string,
    price: number
}
//子接口
type DisGoodsType2 = GoodsType2 & {
    disPrice: number
}
let goods2: DisGoodsType2 = {
    id: '1001',
    price: 200,
    disPrice: 180
}
console.log(goods2.id)
//同名interface会合并
interface Item {
    name: string
}
interface Item {
    age: number
}
let item: Item = {
    name: 'jack',
    age: 18
}
console.log(item.age)
//问题：response对象，如何用type来定义其类型
// {
//     code: 200,
//     msg: 'success',
//     data: {
//         title: '文章标题',
//         content: '文章内容'
//     }
// }
type Data2 = {
    title: string,
    content: string
}
type DisData2 = {
    code: number,
    msg: string,
    data: Data2
}
let res2: DisData2 = {
    code: 120,
    msg: 'success',
    data: {
        title: '文章标题',
        content: '文章内容'
    }
}
console.log(res2.data.content)

//字面量类型
type Gender = '男' | '女'
let gender: Gender = '女'
//问题：还是后端返回数据，这次业务code码有多种情况，1001、1002、1003，尝试改写类型满足要求
// {
//     code: 200,
//     msg: '接口成功'
// }
type data = {
    code: '1001' | '1002' | '1003',
    msg: string
}
let res3: data = {
    code: '1001',
    msg: 'success'
}

//类型推论
let count = 100
count = 200
function add4(a: number, b: number) {
    return a + b
}
const result = add4(1, 3)

//any类型
let foo: any
foo = 100
foo = 'string'
foo = false
let bar: number
bar = foo

//类型断言
const link1 = document.getElementById('link') as HTMLAnchorElement
//link1.href
//类型断言注意事项
function log(foo: string | number) {
    console.log((foo as number).toFixed(2))
}
log(100)
//log('100')

//泛型
//1.定义泛型，把可变的部分抽象成泛型参数T
interface ResData<T> {
    code: number
    msg: string
    data: T
}
//2.定义具体类型，然后使用泛型的时候传入到参数的位置
interface User {
    name: string
    age: number
}
interface Goods2 {
    id2: number
    goodsName: string
}
let user: ResData<User> = {
    code: 200,
    msg: 'success',
    data: {
        name: 'jack',
        age: 18
    }
}
let GoodsData: ResData<Goods2> = {
    code: 300,
    msg: 'false',
    data: {
        id2: 1002,
        goodsName: 'jack'
    }
}
//泛型别名
//1.定义泛型，把可变的部分抽象成泛型参数T
type ResData2<T> = {
    code: number
    msg: string
    data: T
}
//2.定义具体类型，然后使用泛型的时候传入到参数的位置
type User2 = {
    name: string
    age: number
}
type Goods3 = {
    id2: number
    goodsName: string
}
let user2: ResData2<User2> = {
    code: 200,
    msg: 'success',
    data: {
        name: 'jack',
        age: 18
    }
}
let GoodsData2: ResData2<Goods3> = {
    code: 300,
    msg: 'false',
    data: {
        id2: 1002,
        goodsName: 'jack'
    }
}
//泛型用于函数
//原函数
// function createArray(length, value) {
//     let result = []
//     for (let i = 0; i < length; i++) {
//         result[i] = value
//     }
//     return result
// }
//泛型函数
function createArray2<T>(length: number, value: T) {
    let result = []
    for (let i = 0; i < length; i++) {
        result[i] = value
    }
    return result
}
createArray2<number>(4, 100)
createArray2<string>(4, '100')
//泛型约束
function logLen<T>(obj: T) {
    console.log(obj.length)
}
interface LengthObj {
    length: number
}
function logLen1<T extends LengthObj>(obj: T) {
    console.log(obj.length)
}
logLen1({length: 10})
logLen1(['100'])
//logLen1(100)

//综合案例
//先写主线逻辑，再写细节
function updateData() {
    //1.获取最新列表，取到最后一项
    const list = getList()
    const lastItem = list[list.length - 1]
    //2.在上一条记录的基础上count+1，配合当前时间添加到列表尾部
    list.push({
        count: lastItem ? lastItem.count + 1 : 1,
        time: getFormatTime()
    })
    //3.最新列表渲染到页面
    render(list)
    //4.最新列表存入本地
    setList(list)
}
type Item2 = {
    count: number
    time: string
}
const KEY = 'ts-key'
function getList(): Item2[] {
    return JSON.parse(localStorage.getItem(KEY) || '[]')
}
// console.log(getList())
function setList(list: Item2[]) {
    localStorage.setItem(KEY, JSON.stringify(list))
}
//setList([{count: 1, time: '10:10:10'}])
function getFormatTime() {
    const _time = new Date()
    const h = _time.getHours()
    const m = _time.getMinutes()
    const s = _time.getSeconds()
    return `${h}: ${m}:${s}`
}
//console.log(getFormatTime())
function render(list: Item2[]) {
    //1.把list中每个对象格式化成字符串
    const strArr = list.map((item) => {
        return `刷新次数为:${item.count},刷新时间为：${item.time}`
    })
    //2.调用dom api渲染到页面中
    const app = document.getElementById('app') as HTMLDivElement
    app.innerHTML = strArr.join('<br>')
}
updateData()
